package pATT.profilingAlgorithm.PersonalSearcher;
/***************************************************************************
 *                                TermVector                               *
 *                       initial version by: Ryan Scherle                  *
 *                      copyright (C) 2000 IGLU Research Group             *
 *                             rscherle@acm.org                            *
 ***************************************************************************/
/***************************************************************************
 * The licence for this code is included in LICENSE.txt.  If you did not   *
 * get this file with this distribution, request a copy from               *
 * iglu@cs.indiana.edu                                                     *
 ***************************************************************************/

import java.io.IOException;
import java.io.Serializable;
import java.util.Iterator;
import java.util.StringTokenizer;

public class TermVector extends Object implements Serializable, Cloneable
{
  /**
	 * 
	 */
	private static final long serialVersionUID = 1L;
private Object termsId=null;
  private static ObjectPager cache=new RAMObjectPager();

	/*
	 public static void setObjectPager(ObjectPager newObjectPager) {
    cache = newObjectPager;
	 }*/

 	public TermVector overlappingWords(TermVector t){

		TermVector overlap = new TermVector();

		for (Iterator i=t.termIterator(); i.hasNext(); ) {
			String s = i.next().toString();
			if (contains(s)){
				overlap.put(s,1);
			}
		}
		return overlap;
	}
	

	public boolean includeAllWords(TermVector t){

		boolean included = true;
		for (Iterator i=t.termIterator(); i.hasNext(); ) {
			String s = i.next().toString();
			if (!(this.contains(s)))
				included = false;
		}
		return included;
	}

  private ValueSortedMap getAllTerms() {
    if (termsId==null) {
      ValueSortedMap vsm = new ValueSortedMap();
      termsId=cache.store(vsm);
      return vsm;
    } else {
      return (ValueSortedMap)cache.retrieve(termsId);
    }
  }

  private void setAllTerms(ValueSortedMap vsm) {
    if (termsId==null) throw new RuntimeException("Set before getting");
    cache.update(vsm, termsId);
  }
    
  public TermVector()
  {/*do nothing*/  }

  public TermVector(String someWords)
  {
    ValueSortedMap allTerms = getAllTerms();
    
   StringTokenizer st = new StringTokenizer(someWords,
					     IOTools.GENERIC_WORD_DELIMITERS);
    while(st.hasMoreTokens())
      {
	String thisWord = st.nextToken();
	allTerms.put(thisWord.toLowerCase(), 1);
      }

    setAllTerms(allTerms);
  }

  public TermVector(String someWords, String someDelimeters) {
    ValueSortedMap allTerms = getAllTerms();
    allTerms = new ValueSortedMap();
    StringTokenizer st = new StringTokenizer(someWords,
					     someDelimeters);
    while(st.hasMoreTokens())
      {
	String thisWord = st.nextToken();
	allTerms.put(thisWord.toLowerCase(), 1);
      }
    setAllTerms(allTerms);
  }
  public void clear() {
    ValueSortedMap allTerms = getAllTerms();
    allTerms.clear();
    setAllTerms(allTerms);
  }

  public int size() {
    ValueSortedMap allTerms = getAllTerms();
    return allTerms.size();
  }
  
    public synchronized void remove(String term){
    ValueSortedMap allTerms = getAllTerms();
			allTerms.remove(term);
	}
  public synchronized void put(String term, double value){
    ValueSortedMap allTerms = getAllTerms();
	if (term.length() > 0){
	    if(value == 0){
			allTerms.remove(term);
		}
		else{
			allTerms.put(term, value);
		}
    	setAllTerms(allTerms);
	}
  }

  public synchronized boolean contains(String term){
    ValueSortedMap allTerms = getAllTerms();
    return allTerms.containsKey(term);
  }

  public synchronized void putAll(TermVector additional) {
    ValueSortedMap allTerms = getAllTerms();
    if (additional != null)    {
	Iterator addIt = additional.termIterator();
	
	while(addIt.hasNext()) {
	    String newTerm = (String) addIt.next();
	    double newValue = additional.get(newTerm) + get(newTerm);
	    allTerms.put(newTerm, newValue);
	  }
    }
    setAllTerms(allTerms);
  }

  public synchronized void putAllDF(TermVector additional){
    ValueSortedMap allTerms = getAllTerms();
    if (additional != null)
      {
	Iterator addIt = additional.termIterator();
	
	while(addIt.hasNext())
	  {
	    String newTerm = (String) addIt.next();
	    double newValue = 1 + get(newTerm);
	    allTerms.put(newTerm, newValue);
	  }
      }
    setAllTerms(allTerms);
  }

  
  public synchronized void removeAll(TermVector toremove)
  {
    ValueSortedMap allTerms = getAllTerms();
    if (toremove != null)
      {
		Iterator addIt = toremove.termIterator();
	
		while(addIt.hasNext())
		  {
	    	String newTerm = (String) addIt.next();
	    	double newValue = get(newTerm)  - toremove.get(newTerm) ;
			if (newValue >0)
	    		allTerms.put(newTerm, newValue);
	  	  }
      }
    setAllTerms(allTerms);
  }

  public synchronized void removeAllDF(TermVector toremove)
  {
    ValueSortedMap allTerms = getAllTerms();
    if (toremove != null)
      {
	Iterator addIt = toremove.termIterator();
	
	while(addIt.hasNext())
	  {
	    String newTerm = (String) addIt.next();
	    double newValue = get(newTerm)-1;
		if (newValue >0)
	    	allTerms.put(newTerm, newValue);
	  }
      }
    setAllTerms(allTerms);
  }

  public void increment(String term)
  {
    ValueSortedMap allTerms = getAllTerms();
    allTerms.put(term, allTerms.getDouble(term) + 1);
    setAllTerms(allTerms);
  }

  public double get(String term)
  {
    ValueSortedMap allTerms = getAllTerms();
    return allTerms.getDouble(term);
  }

  public synchronized void normalizeSquare()
  {
    ValueSortedMap allTerms = getAllTerms();
    allTerms.normalizeSquare();
    setAllTerms(allTerms);
  }
  
   public synchronized void normalizeAugmented()
  {
    ValueSortedMap allTerms = getAllTerms();
    allTerms.normalizeAugmented();
    setAllTerms(allTerms);
  }

  public synchronized void scaleBy(double n)
  {
    ValueSortedMap allTerms = getAllTerms();
    allTerms.scaleBy(n);
    setAllTerms(allTerms);
  }

  public synchronized void linearlyScale()
  {
    ValueSortedMap allTerms = getAllTerms();
    allTerms.linearlyScale();
    setAllTerms(allTerms);
  }

  public synchronized void subtract(TermVector subWords)
  {
    ValueSortedMap allTerms = getAllTerms();
    Iterator termIt = subWords.termIterator();
    while(termIt.hasNext())
      {
	String theTerm = (String)termIt.next();
	allTerms.remove(theTerm);
      }
    setAllTerms(allTerms);
  }

  public synchronized void subtract(String theTerm)
  {
    ValueSortedMap allTerms = getAllTerms();
    allTerms.remove(theTerm);
    setAllTerms(allTerms);
  }

  public Iterator termIterator()
  {
    ValueSortedMap allTerms = getAllTerms();
    return allTerms.keyIterator();
  }

  public synchronized void truncateTo(int numTerms)
  {
    ValueSortedMap allTerms = getAllTerms();
    allTerms.truncateTo(numTerms);
    setAllTerms(allTerms);
  }
  
  public Object clone()
  {
    TermVector result = new TermVector();
    result.putAll(this);

    return result;
  }
  
  public String toString()
  {
    ValueSortedMap allTerms = getAllTerms();
    return allTerms.toString();
  }

  public TermVector topN(int n) {
    TermVector tv = (TermVector)this.clone();
    tv.truncateTo(n);
    return tv;
  }
  
  public boolean equals(Object o)
  {
    if(!(o instanceof TermVector))
      return false;

    TermVector vec2 = (TermVector)o;

    if(size() != vec2.size())
      return false;
    
    Iterator it = (vec2.termIterator());
    while(it.hasNext())
      {
	String thisTerm = (String)it.next();
	if(get(thisTerm) != vec2.get(thisTerm))
	  return false;
      }

    return true;
  }
  
  public double dice(TermVector tv) {
    double numerator=0, sum1=0, sum2=0;

    if (size()==0 && tv.size()==0) return 1.0;
    if (size()==0 || tv.size()==0) return 0.0;

    for (Iterator i=termIterator(); i.hasNext(); ) {
      String s = i.next().toString();
      numerator+=get(s)*tv.get(s);
      sum1+=get(s);
    }
    for (Iterator i=tv.termIterator(); i.hasNext(); ) {
      double a = tv.get(i.next().toString());
      sum2+=a;
    }
    return ((2 * numerator)/(sum1 + sum2));
  }

  public double euclidean(TermVector tv) {
    double numerator=0;

    if (size()==0 && tv.size()==0) return 1.0;
    if (size()==0 || tv.size()==0) return 0.0;

    for (Iterator i=termIterator(); i.hasNext(); ) {
      String s = i.next().toString();
      double d = 0;
      if (tv.contains(s))
        d = get(s)-tv.get(s);
      else
        d = get(s);
      numerator+=(d * d);
    }

    for (Iterator i=tv.termIterator(); i.hasNext(); ) {
      String w = i.next().toString();
      if (!contains(w)){
        double d = 0 - tv.get(w);
        numerator+=(d * d);
      }
    }

    return Math.sqrt(numerator);
  }

  public double jaccard(TermVector tv) {
    double numerator=0, sum1=0, sum2=0;

    if (size()==0 && tv.size()==0) return 1.0;
    if (size()==0 || tv.size()==0) return 0.0;

    for (Iterator i=termIterator(); i.hasNext(); ) {
      String s = i.next().toString();
      numerator+=get(s)*tv.get(s);
      sum1+=get(s);
    }

    for (Iterator i=tv.termIterator(); i.hasNext(); ) {
      double a = tv.get(i.next().toString());
      sum2+=a;
    }

    return (numerator /(sum1 + sum2 - numerator));
  }

  /////////////////////////////////////////////////////////////////
  /**
     Gives the 2-norm (euclidean distance) between this vector and
     the given one.  If both of the vectors are empty, it returns 1.
     If only one of the vectors is empty, it returns 0.
  **/
  public double twoNorm(TermVector tv) {
    double numerator=0, sum1=0, sum2=0;

    if (size()==0 && tv.size()==0) return 1.0;
    if (size()==0 || tv.size()==0) return 0.0;

    for (Iterator i=termIterator(); i.hasNext(); ) {
      String s = i.next().toString();
      numerator+=get(s)*tv.get(s);
      sum1+=get(s)*get(s);
    }

    for (Iterator i=tv.termIterator(); i.hasNext(); ) {
      double a = tv.get(i.next().toString());
      sum2+=a*a;
    }

    return numerator/Math.sqrt(sum1*sum2);
  }

  

  public double dot(TermVector tv) {
    double denominator=0;

    if (size()==0 && tv.size()==0) return 1.0;
    if (size()==0 || tv.size()==0) return 0.0;

    for (Iterator i=termIterator(); i.hasNext(); ) {
		String s = i.next().toString();
		double w1 = get(s);
		if (tv.contains(s)){
			double w2 = tv.get(s);
			denominator = denominator + (w2*w1);
		}
	}
	return denominator;
  }
  
  public TermVector dotVector(TermVector tv) {
    double denominator=0;

TermVector newtv = new TermVector();

    for (Iterator i=termIterator(); i.hasNext(); ) {
		String s = i.next().toString();
		double w1 = get(s);
		if (tv.contains(s)){
			double w2 = tv.get(s);
			denominator = denominator + (w2*w1);
			double nd = w2*w1;
			newtv.put(s,nd);
		}
	}
	return newtv;
  }
  
  
  public double dissimilarity(TermVector tv) {
  	return this.dot(tv);
  }

/*
    public double dissimilarity(TermVector tv) {
    double denominator=0;

    if (size()==0 && tv.size()==0) return 1.0;
    if (size()==0 || tv.size()==0) return 0.0;

    TermVector t = new TermVector();
    
    for (Iterator i=termIterator(); i.hasNext(); ) {
		String s = i.next().toString();
		double w1 = get(s);
		if (tv.contains(s)){
			double w2 = tv.get(s);
			double w = w1-w2;
			t.put(s,w);
		}
		else
			t.put(s,w1);
	}
	
    for (Iterator i2=tv.termIterator(); i2.hasNext(); ) {
	String s = i2.next().toString();
	if (!this.contains(s)){
		double w2 = tv.get(s)*-1;
		t.put(s,w2);
	}
	}
	return Math.pow(t.getLength(),2);
    }
*/
  /////////////////////////////////////////////////////////////////
  /**
     Gives the cosine distance between this vector and
     the given one.
  **/
  
  public double cosine(TermVector tv) {
	return this.cosine(tv, new TermVector());
  }

  public double cosine(TermVector tv, TermVector toRemove) {
    double num1=0, num2=0, denominator=0;
    if (size()==0 && tv.size()==0) return 1.0;
    if (size()==0 || tv.size()==0) return 0.0;
    for (Iterator i=termIterator(); i.hasNext(); ) {
	String s = i.next().toString();
	if (!toRemove.contains(s)){
		double w1 = get(s);
		num1 = num1 + (w1*w1);
					
		if (tv.contains(s)){
			double w2 = tv.get(s);
			denominator = denominator + (w2*w1);
		}
	}
    }
    for (Iterator i2=tv.termIterator(); i2.hasNext(); ) {
	String s = i2.next().toString();
	if (!toRemove.contains(s)){
		double w2 = tv.get(s);
		num2 = num2 + (w2*w2);
	}
}
    
    return denominator / ( (Math.sqrt(num1) * Math.sqrt(num2)));
  }

	public double overlap(TermVector tv) {
		double sum=0;

		if (size()==0 || tv.size()==0) return 0.0;

		for (Iterator i=termIterator(); i.hasNext(); ) {
			String s = i.next().toString();
			double w1 = get(s);
			if (tv.contains(s)){
				double w2 = tv.get(s);
				sum = sum + (w1 * w2);
			}
		}
		return sum;
  }

  protected void finalize() throws IOException {
    if (termsId!=null) cache.delete(termsId);
  }

  private void writeObject(java.io.ObjectOutputStream out)
    throws IOException {
    out.writeObject(getAllTerms());
  }

  private void readObject(java.io.ObjectInputStream in)
    throws IOException, ClassNotFoundException {
    ValueSortedMap vsm = (ValueSortedMap)in.readObject();
    ValueSortedMap vsmr = getAllTerms();
    vsmr.clear();
    vsmr.putAll(vsm);
    setAllTerms(vsmr);
  }

  public double compareIntersection(TermVector instance_words){
		TermVector new_vector = new TermVector();

		for (Iterator i=termIterator(); i.hasNext(); ) {
			String s = i.next().toString();
			double d = instance_words.get(s);
			new_vector.put(s,d);
		}
		if (new_vector.size()>0)
			return this.cosine(new_vector);
		else
			return 0;
	}
	
  public double getLength() {
    double squaredWeightSum = 0.0;

	Iterator addIt = this.termIterator();
	
	while(addIt.hasNext())
	{
		String thisItem = (String)addIt.next();
		double value = this.get(thisItem);
		squaredWeightSum += (value * value);
	}
	return Math.sqrt(squaredWeightSum);
  }
	public TermVector getNormalized(int n){
		TermVector tnew = new TermVector();
	
		for (Iterator i=termIterator(); i.hasNext(); ) {
    		String s = i.next().toString();
    		double norm_value = get(s) / n;
    		tnew.put(s,norm_value);
		}
	    return tnew;
	}
	
}
